/*
 * Copyright 2015-2016 Tumblr, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tumblr.backboard.imitator;

import com.facebook.rebound.Spring;

/**
 * Perturbs a based on some other source,controls how the source value maps to the spring. Track and follow strategies
 * are hints to subclasses as to the desired behavior of the mapping.
 *
 * @author ericleong
 * @since 2021-05-07
 */
public abstract class Imitator {
    /**
     * Directly map the imitatee to the spring.
     */
    public static final int TRACK_ABSOLUTE = 1;

    /**
     * Map the change in the imitatee to the spring.
     */
    public static final int TRACK_DELTA = 2;
    /**
     * Map the imitatee to the current value of the spring.
     */
    public static final int FOLLOW_EXACT = 1;

    /**
     * Map the imitatee to the end value of the spring.
     */
    public static final int FOLLOW_SPRING = 2;
    /**
     * Whether or not the spring will simply connect the object to the property we are imitating.
     */
    protected int mFollowStrategy = FOLLOW_EXACT;

    /**
     * Whether or not the spring will simply connect the object to the property we are imitating.
     */
    protected int mTrackStrategy = TRACK_ABSOLUTE;

    /**
     * The spring to perturb.
     */
    protected Spring mSpring;
    /**
     * The desired rest value of the spring when not being perturbed.
     */
    protected double mRestValue;

    /**
     * Intended to be used as part of a builder. The spring must be set with {@link #setSpring(Spring)}.
     *
     * @param restValue the rest value for the spring.
     * @param trackStrategy the tracking strategy.
     * @param followStrategy the follow strategy.
     */
    protected Imitator(final double restValue, int trackStrategy, int followStrategy) {
        this(null, restValue, trackStrategy, followStrategy);
    }

    /**
     * Constructor. Uses {@link #TRACK_ABSOLUTE} and {@link #FOLLOW_EXACT}.
     *
     * @param spring the spring to use.
     */
    protected Imitator(Spring spring) {
        this(spring, TRACK_ABSOLUTE, FOLLOW_EXACT);
    }

    /**
     * Constructor.
     *
     * @param spring the spring to use.
     * @param trackStrategy the tracking strategy.
     * @param followStrategy the follow strategy.
     */
    protected Imitator(Spring spring, int trackStrategy, int followStrategy) {
        this(spring, spring.getEndValue(), trackStrategy, followStrategy);
    }

    /**
     * Constructor.
     *
     * @param spring the spring to use.
     * @param restValue the rest value for the spring.
     * @param trackStrategy the tracking strategy.
     * @param followStrategy the follow strategy.
     */
    protected Imitator(Spring spring, double restValue, int trackStrategy, int followStrategy) {
        mSpring = spring;
        mRestValue = restValue;
        mTrackStrategy = trackStrategy;
        mFollowStrategy = followStrategy;
    }

    /**
     * map to spring
     *
     * @param motionValue Maps the value we are tracking to the value of the spring.
     * @return the new end value of the spring. If set to FOLLOW_EXACT, it is also the current value of the spring.
     */
    protected abstract double mapToSpring(float motionValue);

    /**
     * getTrackStrategy
     *
     * @return mTrackStrategy
     */
    public int getTrackStrategy() {
        return mTrackStrategy;
    }

    /**
     * set track strategy
     *
     * @param trackStrategy the desired tracking strategy
     * @return this object for chaining
     */
    public Imitator setTrackStrategy(final int trackStrategy) {
        mTrackStrategy = trackStrategy;
        return this;
    }

    /**
     * get follow strategy
     *
     * @return mFollowStrategy
     */
    public int getFollowStrategy() {
        return mFollowStrategy;
    }

    /**
     * set follow strategy
     *
     * @param followStrategy the desired follow strategy
     * @return this object for chaining
     */
    public Imitator setFollowStrategy(final int followStrategy) {
        mFollowStrategy = followStrategy;
        return this;
    }

    /**
     * getSpring
     *
     * @return mSpring
     */
    public Spring getSpring() {
        return mSpring;
    }

    /**
     * set spring
     *
     * @param spring the spring to set (sets spring's current to the rest value)
     */
    public void setSpring(Spring spring) {
        mSpring = spring;
        if (mSpring != null) {
            // Start spring at rest.
            mSpring.setCurrentValue(mRestValue, true);
        }
    }
}
